Support cross compiling to triples without dylibs
authorAlex Crichton <alex@alexcrichton.com>
Thu, 28 Aug 2014 19:46:04 +0000 (12:46 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 2 Sep 2014 18:46:08 +0000 (11:46 -0700)
Discovering the prefix/suffix needs to understand that it's not actually
available.

Closes #442

src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/fingerprint.rs
src/cargo/ops/cargo_rustc/mod.rs
tests/test_cargo_cross_compile.rs

index 6ee234b2a9b36b2a8ce5cf4cb2b0713cd888e132..35714b16384cf406813dea29125bc638393979b5 100644 (file)
@@ -3,6 +3,7 @@ use std::str;
 
 use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target};
 use util::{mod, CargoResult, ChainError, internal, Config, profile, Require};
+use util::human;
 
 use super::{Kind, KindPlugin, KindTarget, Compilation};
 use super::layout::{Layout, LayoutProxy};
@@ -26,9 +27,10 @@ pub struct Context<'a, 'b> {
     host: Layout,
     target: Option<Layout>,
     target_triple: String,
-    host_dylib: (String, String),
+    host_triple: String,
+    host_dylib: Option<(String, String)>,
     package_set: &'a PackageSet,
-    target_dylib: (String, String),
+    target_dylib: Option<(String, String)>,
     target_exe: String,
     requirements: HashMap<(&'a PackageId, &'a str), PlatformRequirement>,
 }
@@ -48,10 +50,11 @@ impl<'a, 'b> Context<'a, 'b> {
         };
         let (rustc_version, rustc_host) = try!(Context::rustc_version());
         let target_triple = config.target().map(|s| s.to_string());
-        let target_triple = target_triple.unwrap_or(rustc_host);
+        let target_triple = target_triple.unwrap_or(rustc_host.clone());
         Ok(Context {
             rustc_version: rustc_version,
             target_triple: target_triple,
+            host_triple: rustc_host,
             env: env,
             host: host,
             target: target,
@@ -93,7 +96,7 @@ impl<'a, 'b> Context<'a, 'b> {
     /// Run `rustc` to discover the dylib prefix/suffix for the target
     /// specified as well as the exe suffix
     fn filename_parts(target: Option<&str>)
-                      -> CargoResult<((String, String), String)> {
+                      -> CargoResult<(Option<(String, String)>, String)> {
         let process = util::process("rustc")
                            .arg("-")
                            .arg("--crate-name").arg("-")
@@ -106,17 +109,22 @@ impl<'a, 'b> Context<'a, 'b> {
         };
         let output = try!(process.exec_with_output());
 
+        let error = str::from_utf8(output.error.as_slice()).unwrap();
         let output = str::from_utf8(output.output.as_slice()).unwrap();
         let mut lines = output.lines();
-        let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
-                                          .split('-').collect();
-        assert!(dylib_parts.len() == 2,
-                "rustc --print-file-name output has changed");
+        let dylib = if error.contains("dropping unsupported crate type `dylib`") {
+            None
+        } else {
+            let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
+                                              .split('-').collect();
+            assert!(dylib_parts.len() == 2,
+                    "rustc --print-file-name output has changed");
+            Some((dylib_parts[0].to_string(), dylib_parts[1].to_string()))
+        };
+
         let exe_suffix = lines.next().unwrap().trim()
                               .split('-').skip(1).next().unwrap().to_string();
-
-        Ok(((dylib_parts[0].to_string(), dylib_parts[1].to_string()),
-            exe_suffix.to_string()))
+        Ok((dylib, exe_suffix.to_string()))
     }
 
     /// Prepare this context, ensuring that all filesystem directories are in
@@ -193,9 +201,17 @@ impl<'a, 'b> Context<'a, 'b> {
     ///
     /// If `plugin` is true, the pair corresponds to the host platform,
     /// otherwise it corresponds to the target platform.
-    fn dylib(&self, kind: Kind) -> (&str, &str) {
-        let pair = if kind == KindPlugin {&self.host_dylib} else {&self.target_dylib};
-        (pair.ref0().as_slice(), pair.ref1().as_slice())
+    fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
+        let (triple, pair) = if kind == KindPlugin {
+            (&self.host_triple, &self.host_dylib)
+        } else {
+            (&self.target_triple, &self.target_dylib)
+        };
+        match *pair {
+            None => return Err(human(format!("dylib outputs are not supported \
+                                              for {}", triple))),
+            Some((ref s1, ref s2)) => Ok((s1.as_slice(), s2.as_slice())),
+        }
     }
 
     /// Return the target triple which this context is targeting.
@@ -204,7 +220,7 @@ impl<'a, 'b> Context<'a, 'b> {
     }
 
     /// Return the exact filename of the target.
-    pub fn target_filenames(&self, target: &Target) -> Vec<String> {
+    pub fn target_filenames(&self, target: &Target) -> CargoResult<Vec<String>> {
         let stem = target.file_stem();
 
         let mut ret = Vec::new();
@@ -214,7 +230,7 @@ impl<'a, 'b> Context<'a, 'b> {
             if target.is_dylib() {
                 let plugin = target.get_profile().is_plugin();
                 let kind = if plugin {KindPlugin} else {KindTarget};
-                let (prefix, suffix) = self.dylib(kind);
+                let (prefix, suffix) = try!(self.dylib(kind));
                 ret.push(format!("{}{}{}", prefix, stem, suffix));
             }
             if target.is_rlib() {
@@ -225,7 +241,7 @@ impl<'a, 'b> Context<'a, 'b> {
             }
         }
         assert!(ret.len() > 0);
-        return ret;
+        return Ok(ret);
     }
 
     /// For a package, return all targets which are registered as dependencies
index 323150f0129703eedc9ebc99c314cc1a599de47d..aebb93ff60cf76604dc357f0953f17f0c84de694 100644 (file)
@@ -73,7 +73,7 @@ pub fn prepare_target(cx: &mut Context, pkg: &Package, target: &Target,
     if !target.get_profile().is_doc() {
         pairs.push((old_dep_info, new_dep_info));
 
-        for filename in cx.target_filenames(target).iter() {
+        for filename in try!(cx.target_filenames(target)).iter() {
             let filename = filename.as_slice();
             let dst = root.join(filename);
             pairs.push((old_root.join(filename), root.join(filename)));
index 6b725426738801e9cbf9f322bb2ced3fadaedf2d..93510285390408173a73da163c67fcd2f421f607 100644 (file)
@@ -135,10 +135,10 @@ fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
     let (mut libs, mut bins) = (Vec::new(), Vec::new());
     for &target in targets.iter() {
         let work = if target.get_profile().is_doc() {
-            vec![(rustdoc(pkg, target, cx), KindTarget)]
+            vec![(try!(rustdoc(pkg, target, cx)), KindTarget)]
         } else {
             let req = cx.get_requirement(pkg, target);
-            rustc(pkg, target, cx, req)
+            try!(rustc(pkg, target, cx, req))
         };
 
         let dst = if target.is_lib() {&mut libs} else {&mut bins};
@@ -188,7 +188,8 @@ fn compile_custom(pkg: &Package, cmd: &str,
 }
 
 fn rustc(package: &Package, target: &Target,
-         cx: &mut Context, req: PlatformRequirement) -> Vec<(Work, Kind)> {
+         cx: &mut Context, req: PlatformRequirement)
+         -> CargoResult<Vec<(Work, Kind)> >{
     let crate_types = target.rustc_crate_types();
     let root = package.get_root();
 
@@ -196,7 +197,7 @@ fn rustc(package: &Package, target: &Target,
          root.display(), target, crate_types, cx.primary, req);
 
     let primary = cx.primary;
-    let rustcs = prepare_rustc(package, target, crate_types, cx, req);
+    let rustcs = try!(prepare_rustc(package, target, crate_types, cx, req));
 
     let _ = cx.config.shell().verbose(|shell| {
         for &(ref rustc, _) in rustcs.iter() {
@@ -205,7 +206,7 @@ fn rustc(package: &Package, target: &Target,
         Ok(())
     });
 
-    rustcs.move_iter().map(|(rustc, kind)| {
+    Ok(rustcs.move_iter().map(|(rustc, kind)| {
         let name = package.get_name().to_string();
 
         (proc() {
@@ -223,32 +224,35 @@ fn rustc(package: &Package, target: &Target,
             }
             Ok(())
         }, kind)
-    }).collect()
+    }).collect())
 }
 
 fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>,
                  cx: &Context, req: PlatformRequirement)
-                 -> Vec<(ProcessBuilder, Kind)> {
+                 -> CargoResult<Vec<(ProcessBuilder, Kind)>> {
     let base = process("rustc", package, cx);
     let base = build_base_args(base, target, crate_types.as_slice());
 
     let target_cmd = build_plugin_args(base.clone(), cx, package, target, KindTarget);
     let plugin_cmd = build_plugin_args(base, cx, package, target, KindPlugin);
-    let target_cmd = build_deps_args(target_cmd, target, package, cx, KindTarget);
-    let plugin_cmd = build_deps_args(plugin_cmd, target, package, cx, KindPlugin);
+    let target_cmd = try!(build_deps_args(target_cmd, target, package, cx,
+                                          KindTarget));
+    let plugin_cmd = try!(build_deps_args(plugin_cmd, target, package, cx,
+                                          KindPlugin));
 
-    match req {
+    Ok(match req {
         Target => vec![(target_cmd, KindTarget)],
         Plugin => vec![(plugin_cmd, KindPlugin)],
         PluginAndTarget if cx.config.target().is_none() =>
             vec![(target_cmd, KindTarget)],
         PluginAndTarget => vec![(target_cmd, KindTarget),
                                 (plugin_cmd, KindPlugin)],
-    }
+    })
 }
 
 
-fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {
+fn rustdoc(package: &Package, target: &Target,
+           cx: &mut Context) -> CargoResult<Work> {
     let kind = KindTarget;
     let pkg_root = package.get_root();
     let cx_root = cx.layout(kind).proxy().dest().join("doc");
@@ -256,7 +260,7 @@ fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {
     let rustdoc = rustdoc.arg(target.get_src_path())
                          .arg("-o").arg(cx_root)
                          .arg("--crate-name").arg(target.get_name());
-    let rustdoc = build_deps_args(rustdoc, target, package, cx, kind);
+    let rustdoc = try!(build_deps_args(rustdoc, target, package, cx, kind));
 
     log!(5, "commands={}", rustdoc);
 
@@ -266,7 +270,7 @@ fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {
 
     let primary = cx.primary;
     let name = package.get_name().to_string();
-    proc() {
+    Ok(proc() {
         if primary {
             try!(rustdoc.exec().chain_error(|| {
                 human(format!("Could not document `{}`.", name))
@@ -278,7 +282,7 @@ fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {
             }))
         }
         Ok(())
-    }
+    })
 }
 
 fn build_base_args(mut cmd: ProcessBuilder,
@@ -355,7 +359,8 @@ fn build_plugin_args(mut cmd: ProcessBuilder, cx: &Context, pkg: &Package,
 }
 
 fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
-                   cx: &Context, kind: Kind) -> ProcessBuilder {
+                   cx: &Context,
+                   kind: Kind) -> CargoResult<ProcessBuilder> {
     enum LinkReason { Dependency, LocalLib }
 
     let layout = cx.layout(kind);
@@ -367,7 +372,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
     cmd = push_native_dirs(cmd, &layout, package, cx, &mut HashSet::new());
 
     for &(_, target) in cx.dep_targets(package).iter() {
-        cmd = link_to(cmd, target, cx, kind, Dependency);
+        cmd = try!(link_to(cmd, target, cx, kind, Dependency));
     }
 
     let mut targets = package.get_targets().iter().filter(|target| {
@@ -376,14 +381,15 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
 
     if target.is_bin() {
         for target in targets {
-            cmd = link_to(cmd, target, cx, kind, LocalLib);
+            cmd = try!(link_to(cmd, target, cx, kind, LocalLib));
         }
     }
 
-    return cmd;
+    return Ok(cmd);
 
     fn link_to(mut cmd: ProcessBuilder, target: &Target,
-               cx: &Context, kind: Kind, reason: LinkReason) -> ProcessBuilder {
+               cx: &Context, kind: Kind,
+               reason: LinkReason) -> CargoResult<ProcessBuilder> {
         // If this target is itself a plugin *or* if it's being linked to a
         // plugin, then we want the plugin directory. Otherwise we want the
         // target directory (hence the || here).
@@ -393,7 +399,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
             KindTarget => KindTarget,
         });
 
-        for filename in cx.target_filenames(target).iter() {
+        for filename in try!(cx.target_filenames(target)).iter() {
             let mut v = Vec::new();
             v.push_all(target.get_name().as_bytes());
             v.push(b'=');
@@ -405,7 +411,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
             v.push_all(filename.as_bytes());
             cmd = cmd.arg("--extern").arg(v.as_slice());
         }
-        return cmd;
+        return Ok(cmd);
     }
 
     fn push_native_dirs(mut cmd: ProcessBuilder, layout: &layout::LayoutProxy,
index 35b702a823955efdcce8f27726f4e32f705631cb..ab5c2ec5915ff0be6180020dc40e869991504302 100644 (file)
@@ -456,3 +456,22 @@ test!(simple_cargo_run {
     assert_that(p.cargo_process("run").arg("--target").arg(target),
                 execs().with_status(0));
 })
+
+test!(cross_but_no_dylibs {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.0"
+            authors = []
+
+            [lib]
+            name = "foo"
+            crate-type = ["dylib"]
+        "#)
+        .file("src/lib.rs", "");
+    assert_that(p.cargo_process("build").arg("--target").arg("arm-apple-ios"),
+                execs().with_status(101)
+                       .with_stderr("dylib outputs are not supported for \
+                                     arm-apple-ios"));
+})